
#include <iostream>
#include "gameState.h"


bool GameState::movePossible(const Move &m)
{
    std::vector<Move> possibleMoves = this->generateAllMoves(); 
    
    for(int i = 0; i < possibleMoves.size(); i++)
        if(m == possibleMoves[i])
            return true;
    
    return false;
}

std::vector<Move> GameState::generateAllSimpleMoves(Colour playerSide, bool generateAlsoAttackMoves) const
{
    std::vector<Move> moves;
            
    for(int i = 0; i < 8; i++)
        for(int j = 0; j < 8; j++)
        {
            std::vector<Move> tmp = move(i,j, playerSide, generateAlsoAttackMoves);
            merge(moves, tmp);   
        }
      
    return moves;
}

std::vector<Move> GameState::movePawn(int row, int col, bool generateAlsoAttackMoves) const
{    
    std::vector<Move> moves;
    FieldValue pawn = board[row][col];
    int trow, tcol;
    
    // Cannot make o move to the end of the board witout promotion.
    
    if(pawn == BLACK_PAWN)
    {        
        // check if we can move by two positions
        if(row == 1 && board[row+1][col] == EMPTY && board[row+2][col] == EMPTY )
            moves.push_back(Move(row, col, row+2,col, ENPASSANT_FIRST_STAGE));
               
        if(inBoard(row+1, col) && board[row+1][col] == EMPTY)
            moves.push_back(Move(row, col, row+1,col, COMMON));
            
        // check if beating is possible
        if (  ( inBoard(row+1,col-1) && Util::pieceColour(board[row+1][col-1]) == WHITE ) ||
              ( inBoard(row+1,col-1) && generateAlsoAttackMoves && board[row+1][col-1] == EMPTY) )
            moves.push_back(Move(row, col, row+1,col-1, COMMON));
            
        if( ( inBoard(row+1,col+1) && Util::pieceColour(board[row+1][col+1]) == WHITE) || 
            ( inBoard(row+1,col+1) && generateAlsoAttackMoves && board[row+1][col+1] == EMPTY)  )
            moves.push_back(Move(row, col, row+1,col+1, COMMON));
    }
    else if(pawn == WHITE_PAWN)
    {
        // check if we can move two positions
        if(row == 6 && board[row-1][col] == EMPTY && board[row-2][col] == EMPTY )
            moves.push_back(Move(row, col, row-2,col, ENPASSANT_FIRST_STAGE));
        
        if(inBoard(row-1, col) && board[row-1][col] == EMPTY)
            moves.push_back(Move(row, col, row-1,col, COMMON));
            
        // check if beating is possible
        if( (inBoard(row-1,col-1) && Util::pieceColour(board[row-1][col-1]) == BLACK) ||
            (inBoard(row-1,col-1) && generateAlsoAttackMoves && board[row-1][col-1] == EMPTY) )
            moves.push_back(Move(row, col, row-1,col-1, COMMON));
            
        if( (inBoard(row-1,col+1) && Util::pieceColour(board[row-1][col+1]) == BLACK) || 
            (inBoard(row-1,col+1) && generateAlsoAttackMoves && board[row-1][col+1] == EMPTY)  )
            moves.push_back(Move(row, col, row-1,col+1, COMMON));
    }
    
    return moves;
}

std::vector<Move> GameState::moveKnight(int row, int col, Colour movedPiece) const
{
    std::vector<Move> moves;
    
    int directions[8][2] = { {-2,-1}, {-2,1}, {2,-1}, {2,1}, {-1,-2}, {1,-2}, {-1,2}, {1,2} };
    int trow, tcol;
    
    for(int i = 0; i < 8; i++)
    {
        trow = row - directions[i][0];
        tcol = col - directions[i][1];
        if(inBoard(trow, tcol) && Util::pieceColour(board[trow][tcol]) != movedPiece)   
            moves.push_back(Move(row, col, trow, tcol, COMMON));
    }
    
    return moves;
}


std::vector<Move> GameState::moveBishop(int row, int col, Colour movedPiece) const
{
    std::vector<Move> moves;
    
    int trow, tcol;
    int directions[4][2] = { {-1,-1}, {-1,1}, {1,-1}, {1,1}  };
    
    for(int i = 0; i < 4; i++)
    {
                // go to every direction
        trow = row + directions[i][0];
        tcol = col + directions[i][1];

        while( inBoard(trow, tcol) && board[trow][tcol] == EMPTY )
        {
            moves.push_back(Move(row, col, trow, tcol, COMMON));
            trow += directions[i][0];   
            tcol += directions[i][1];
        }
        
        if(inBoard(trow, tcol) && Util::pieceColour(board[trow][tcol]) != movedPiece )
            moves.push_back(Move(row, col, trow, tcol, COMMON));
    }
    return moves;
}
        

std::vector<Move> GameState::moveRook(int row, int col, Colour movedPiece) const
{
    std::vector<Move> moves;
    
    int trow, tcol;
    int directions[4][2] = { {-1,0}, {1,0}, {0,-1}, {0,1}  };
    
    for(int i = 0; i < 4; i++)
    {
        // go to every direction
        trow = row + directions[i][0];
        tcol = col + directions[i][1];

        while( inBoard(trow, tcol) && board[trow][tcol] == EMPTY )
        {
            moves.push_back(Move(row, col, trow, tcol, COMMON));
            trow += directions[i][0];   
            tcol += directions[i][1];
        }
        
        if(inBoard(trow, tcol) && Util::pieceColour(board[trow][tcol]) != movedPiece )
            moves.push_back(Move(row, col, trow, tcol, COMMON));
    }
    return moves;
}
        
std::vector<Move> GameState::moveQueen(int row, int col, Colour movedPiece) const
{
    std::vector<Move> moves;
            
    std::vector<Move> moveB = moveBishop(row, col, movedPiece);
    std::vector<Move> moveR = moveRook(row, col, movedPiece);
    merge(moves, moveB);
    merge(moves, moveR);
    return moves;
}
        
std::vector<Move> GameState::moveKing(int row, int col, Colour movedPiece) const
{
    std::vector<Move> moves;
            
    int trow, tcol;
    int directions[8][2] = { {0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1} };
    for(int i = 0; i < 8; i++)
    {
        trow = row + directions[i][0];
        tcol = col + directions[i][1];
        if( inBoard(trow, tcol) && Util::pieceColour(board[trow][tcol]) != movedPiece )
            moves.push_back(Move(row, col, trow, tcol, COMMON));  
    }
            
    return moves;
}
        
        
std::vector<Move> GameState::move(int row, int col, Colour movedPiece, bool generateAlsoAttackMoves) const
{
    std::vector<Move> moves;
            
    if(movedPiece == BLACK)
    {
        switch(board[row][col])
        {
            case BLACK_PAWN :
                moves = movePawn(row, col, generateAlsoAttackMoves);
                break;
            case BLACK_KNIGHT :
                moves = moveKnight(row, col, movedPiece);
                break;
            case BLACK_BISHOP :
                moves = moveBishop(row, col, movedPiece);
                break;
            case BLACK_ROOK :
                moves = moveRook(row, col, movedPiece);
                break;
            case BLACK_QUEEN :
                moves = moveQueen(row, col, movedPiece);
                break;
            case BLACK_KING :
                moves = moveKing(row, col, movedPiece);
                break;
        }
    }
    else if(movedPiece == WHITE)
    {
        switch(board[row][col])
        {
            case WHITE_PAWN :
                moves = movePawn(row, col, generateAlsoAttackMoves);
                break;
            case WHITE_KNIGHT :
                moves = moveKnight(row, col, movedPiece);
                break;
            case WHITE_BISHOP :
                moves = moveBishop(row, col, movedPiece);
                break;                      
            case WHITE_ROOK :
                moves = moveRook(row, col, movedPiece);
                break;
            case WHITE_QUEEN :
                moves = moveQueen(row, col, movedPiece);
                break;
            case WHITE_KING :
                moves = moveKing(row, col, movedPiece);
                break;
        }
    }
                
    return moves;
}


std::vector<Move> GameState::moveEnPassant() const
{
    std::vector<Move> moves;
    
    if(lastMoveEnPassantFirstStage && round == WHITE && inBoard(pawnTwoRow,pawnTwoCol-1) && board[pawnTwoRow][pawnTwoCol-1] == WHITE_PAWN)
        moves.push_back(Move(pawnTwoRow,pawnTwoCol-1,pawnTwoRow-1,pawnTwoCol,ENPASSANT_SECOND_STAGE));
        
    if(lastMoveEnPassantFirstStage && round == WHITE && inBoard(pawnTwoRow,pawnTwoCol+1) && board[pawnTwoRow][pawnTwoCol+1] == WHITE_PAWN)
        moves.push_back(Move(pawnTwoRow,pawnTwoCol+1,pawnTwoRow-1,pawnTwoCol,ENPASSANT_SECOND_STAGE));
    
    if(lastMoveEnPassantFirstStage && round == BLACK && inBoard(pawnTwoRow,pawnTwoCol-1) && board[pawnTwoRow][pawnTwoCol-1] == BLACK_PAWN)
        moves.push_back(Move(pawnTwoRow,pawnTwoCol-1,pawnTwoRow+1,pawnTwoCol,ENPASSANT_SECOND_STAGE));
        
    if(lastMoveEnPassantFirstStage && round == BLACK && inBoard(pawnTwoRow,pawnTwoCol+1) && board[pawnTwoRow][pawnTwoCol+1] == BLACK_PAWN)
        moves.push_back(Move(pawnTwoRow,pawnTwoCol+1,pawnTwoRow+1,pawnTwoCol,ENPASSANT_SECOND_STAGE));
    
    return moves;
}

// zwykly ruch pionek do konca usunac
std::vector<Move> GameState::movePromotion() const
{
    std::vector<Move> moves;
    
    if(round == WHITE)
    {
        for(int i = 0; i < 8; i++)
        {
            if(board[1][i] == WHITE_PAWN && board[0][i] == EMPTY)
            {
                    moves.push_back(Move(1,i,0,i,PROMOTION_FOR_ROOK));   
                    moves.push_back(Move(1,i,0,i,PROMOTION_FOR_KNIGHT));   
                    moves.push_back(Move(1,i,0,i,PROMOTION_FOR_BISHOP));   
                    moves.push_back(Move(1,i,0,i,PROMOTION_FOR_QUEEN));   
            }
            
            if( board[1][i] == WHITE_PAWN && inBoard(0,i-1) && Util::pieceColour(board[0][i-1]) == BLACK)
            {
                moves.push_back(Move(1,i,0,i-1,PROMOTION_FOR_ROOK));   
                moves.push_back(Move(1,i,0,i-1,PROMOTION_FOR_KNIGHT));   
                moves.push_back(Move(1,i,0,i-1,PROMOTION_FOR_BISHOP));   
                moves.push_back(Move(1,i,0,i-1,PROMOTION_FOR_QUEEN));
            }
            
            if( board[1][i] == WHITE_PAWN && inBoard(0,i+1) && Util::pieceColour(board[0][i+1]) == BLACK)
            {
                moves.push_back(Move(1,i,0,i+1,PROMOTION_FOR_ROOK));   
                moves.push_back(Move(1,i,0,i+1,PROMOTION_FOR_KNIGHT));   
                moves.push_back(Move(1,i,0,i+1,PROMOTION_FOR_BISHOP));   
                moves.push_back(Move(1,i,0,i+1,PROMOTION_FOR_QUEEN));
            }
        }   
    }
    else if(round == BLACK)
    {
        for(int i = 0; i < 8; i++)
        {
            if(board[6][i] == BLACK_PAWN && board[7][i] == EMPTY)
            {
                moves.push_back(Move(6,i,7,i,PROMOTION_FOR_ROOK));   
                moves.push_back(Move(6,i,7,i,PROMOTION_FOR_KNIGHT));   
                moves.push_back(Move(6,i,7,i,PROMOTION_FOR_BISHOP));   
                moves.push_back(Move(6,i,7,i,PROMOTION_FOR_QUEEN));   
            }  
            
            if(board[6][i] == BLACK_PAWN && inBoard(7, i-1) && Util::pieceColour(board[7][i-1]) == WHITE)
            {
                moves.push_back(Move(6,i,7,i-1,PROMOTION_FOR_ROOK));   
                moves.push_back(Move(6,i,7,i-1,PROMOTION_FOR_KNIGHT));   
                moves.push_back(Move(6,i,7,i-1,PROMOTION_FOR_BISHOP));   
                moves.push_back(Move(6,i,7,i-1,PROMOTION_FOR_QUEEN)); 
            } 
            
            if(board[6][i] == BLACK_PAWN && inBoard(7, i+1) && Util::pieceColour(board[7][i+1]) == WHITE)
            {
                moves.push_back(Move(6,i,7,i+1,PROMOTION_FOR_ROOK));   
                moves.push_back(Move(6,i,7,i+1,PROMOTION_FOR_KNIGHT));   
                moves.push_back(Move(6,i,7,i+1,PROMOTION_FOR_BISHOP));   
                moves.push_back(Move(6,i,7,i+1,PROMOTION_FOR_QUEEN)); 
            } 
        }   
    }
    return moves;
}


std::vector<Move> GameState::generateAllMoves() const
{
    std::vector<Move> moves;
    
    if(drawRuleCount >= 50)
        return moves;
        
    moves = generateAllSimpleMoves(round, false);
    std::vector<Move> special = generateAllSpecialMoves();   
    merge(moves, special);
    
    std::vector<Move> goodMoves;
    
    for(int i = 0; i < moves.size(); i++)
    {
        GameState tmp = applyMove(moves[i]);
        if(!tmp.isEnemyUnderCheck)
        {
            goodMoves.push_back(moves[i]); 
        }
    }                
    return goodMoves;
}


std::vector<Move> GameState::moveCastling() const
{
    std::vector<Move> moves;       
    
    if(round == WHITE && whiteLeftCastlePossible   // it means that we didnt move the rook and king
       && board[7][1] == EMPTY && board[7][2] == EMPTY && board[7][3] == EMPTY  // empty fileds between
       && attackInfo[1][7][2] == 0 && attackInfo[1][7][3] == 0 && attackInfo[1][7][4] == 0)    // no attack
    {
            moves.push_back(Move(7,4,7,2,LEFTSIDE_CASTLING));
    }
    if(round == WHITE && whiteRightCastlePossible   // it means that we didnt move the rook and king
       && board[7][5] == EMPTY && board[7][6] == EMPTY    // empty fileds between
       && attackInfo[1][7][4] == 0 && attackInfo[1][7][5] == 0 && attackInfo[1][7][6] == 0)    // no attack
    {
            moves.push_back(Move(7,4,7,6,RIGHTSIDE_CASTLING));
    }
    
    
    if(round == BLACK && blackLeftCastlePossible   // it means that we didnt move the rook and king
       && board[0][1] == EMPTY && board[0][2] == EMPTY && board[0][3] == EMPTY  // empty fileds between
       && attackInfo[1][0][2] == 0 && attackInfo[1][0][3] == 0 && attackInfo[1][0][4] == 0)    // no attack
    {
            moves.push_back(Move(0,4,0,2,LEFTSIDE_CASTLING));
    }
    if(round == BLACK && blackRightCastlePossible   // it means that we didnt move the rook and king
       && board[0][5] == EMPTY && board[0][6] == EMPTY    // empty fileds between
       && attackInfo[1][0][4] == 0 && attackInfo[1][0][5] == 0 && attackInfo[1][0][6] == 0)    // no attack
    {
            moves.push_back(Move(0,4,0,6,RIGHTSIDE_CASTLING));
    }
    

    return moves;
}

std::vector<Move> GameState::generateAllSpecialMoves() const
{
    std::vector<Move> moves;
    
    std::vector<Move> tmp = moveEnPassant();
    merge(moves, tmp);  
    tmp = movePromotion(); 
    merge(moves, tmp);
    tmp = moveCastling(); 
    merge(moves, tmp);
    
    return moves;
}


void GameState::getAttackInfo()
{
    std::vector<Move> currentPlayerMoves = generateAllSimpleMoves(round, true);
    std::vector<Move> enemyPlayerMoves = generateAllSimpleMoves(Util::opposite(round), true);
    
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 8; j++)
            for(int k = 0; k < 8; k++)
                attackInfo[i][j][k] = 0;

    int r, c;
                
    for(int i = 0; i < currentPlayerMoves.size(); i++)
    {
        r = currentPlayerMoves[i].destination_row;
        c = currentPlayerMoves[i].destination_col;
        
        // A straight pawn move isn't beating
        if( board[currentPlayerMoves[i].source_row][currentPlayerMoves[i].source_col] == ((round == WHITE) ? WHITE_PAWN : BLACK_PAWN) &&
            c == (int)currentPlayerMoves[i].source_col)
            ;
        else
            attackInfo[0][r][c]++;
    }

          
    for(int i = 0; i < enemyPlayerMoves.size(); i++)
    {
        r = enemyPlayerMoves[i].destination_row;
        c = enemyPlayerMoves[i].destination_col;
        
        // A straight pawn move isn't beating
        if( board[enemyPlayerMoves[i].source_row][enemyPlayerMoves[i].source_col] == ((round == WHITE) ? BLACK_PAWN : WHITE_PAWN) &&
            c == (int)enemyPlayerMoves[i].source_col)
            ;
        else
            attackInfo[1][r][c]++; 
    }
}



